/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package shaman.storage.impl.filter.crypto;

import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.MessageDigestSpi;

/**
 * An RFC2104-compliant HMAC implementation
 * @author dmitriy
 */
public class HmacCalculator extends MessageDigestSpi {

    public static final byte IPAD_BYTE=0x5c;
    public static final byte OPAD_BYTE=0x36;

    private final byte[] key_ipad;
    private final byte[] key_opad;
    private final MessageDigest digester;

    public HmacCalculator(byte[] key, MessageDigest digester, int blocklen) {

        int i;
        int b=blocklen/8;
        byte[] keyBytes=new byte[b];
        key_ipad=new byte[b];
        key_opad=new byte[b];
        int l=digester.getDigestLength();
        digester.reset();
        if (key.length>b)
        {
            System.arraycopy(digester.digest(key), 0, keyBytes, 0, l);
            for (i=l; i<b; i++)
                keyBytes[i]=0;
        }
        else {
            System.arraycopy(key, 0, keyBytes, 0, l);
            for (i=key.length; i<b; i++)
                keyBytes[i]=0;
        }
        for (i=0; i<b; i++)
        {
            key_ipad[i]=(byte) (keyBytes[i]^IPAD_BYTE);
            key_opad[i]=(byte) (keyBytes[i]^OPAD_BYTE);
        }
        this.digester=digester;
    }

    @Override
    public void engineReset() {
        digester.reset();
        digester.update(key_ipad);
    }

    @Override
    public byte[] engineDigest() {
        int digestLen=digester.getDigestLength();
        byte[] digest=new byte[digestLen];
        try {
            engineDigest(digest, 0, digestLen);
        } catch (DigestException ex) {
        }
        return digest;
    }

    @Override
    protected int engineDigest(byte[] buf, int offset, int len) throws DigestException {
        int realLen=digester.getDigestLength();
        if (len<realLen)
            throw new DigestException("Buffer too small for HMAC");
        byte[] stage1=digester.digest();
        digester.update(key_opad);
        digester.update(stage1);
        System.arraycopy(digester.digest(), 0, buf, offset, realLen);
        return realLen;
    }

    @Override
    public int engineGetDigestLength() {
        return digester.getDigestLength();
    }

    @Override
    public void engineUpdate(ByteBuffer input) {
        digester.update(input);
    }

    @Override
    public void engineUpdate(byte input) {
        digester.update(input);
    }

    @Override
    public void engineUpdate(byte[] input, int offset, int len) {
        digester.update(input, offset, len);
    }

    public byte[] digest(byte[] message) {
        engineReset();
        engineUpdate(message, 0, message.length);
        return engineDigest();
    }
}
